package utils

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math/bits"
	"os"
	"runtime/debug"
	"strings"
)

// This project fatal error
func ThisFatal(v any) {
	fmt.Printf("rvld: \033[0;1;31mFatal error:\033[0m %v\n", v)
	debug.PrintStack()
	os.Exit(1)
}

// This project must no error
func ThisMustNo(err error) {
	if err != nil {
		ThisFatal(err)
	}
}

// This project assert
func ThisAssert(condition bool) {
	if !condition {
		ThisFatal("assert failed")
	}
}

// This project read
func ThisRead[T any](data []byte) (val T) {
	reader := bytes.NewReader(data)
	err := binary.Read(reader, binary.LittleEndian, &val)
	ThisMustNo(err)
	return
}

// This project read slice
func ThisReadSlice[T any](data []byte, sz int) []T {
	nums := len(data) / sz
	res := make([]T, 0, nums)
	for nums > 0 {
		res = append(res, ThisRead[T](data))
		data = data[sz:]
		nums--
	}

	return res
}

// This project write method
func ThisWrite[T any](data []byte, e T) {
	buf := &bytes.Buffer{}
	err := binary.Write(buf, binary.LittleEndian, e)
	ThisMustNo(err)
	copy(data, buf.Bytes())
}

// This project remove string prefix
func ThisRemovePrefix(s, prefix string) (string, bool) {
	if strings.HasPrefix(s, prefix) {
		s = strings.TrimPrefix(s, prefix)
		return s, true
	}

	return s, false
}

// This project Remove if algorithm, generic function, filter for array case
func ThisRemoveIf[T any](elems []T, condition func(T) bool) []T {
	i := 0
	for _, elem := range elems {
		// if elem match func `condition`
		if condition(elem) {
			continue
		}
		elems[i] = elem
		i++
	}

	return elems[:i]
}

// This project All zeros algorithm
func ThisAllZeros(bs []byte) bool {
	b := byte(0)
	for _, s := range bs {
		b |= s
		// NOTE: output true when checking bs is all 0
		// if s = 1, b |= s ===> 0 | 1 = 1, this will be discarded
		// if s = 0, b |= s ===> 0 | 0 = 0, this is needed
	}

	return b == 0
}

// This project align to
func ThisAlignTo(val, align uint64) uint64 {
	// if `align` == 0 retun `val`
	if align == 0 {
		return val
	}
	// NOTE:
	// if align != 0, do
	//    val + align - 1
	// &^ align - 1
	// TODO: output what the fuck ?
	return (val + align - 1) &^ (align - 1)
}

// / private func hasSingleBit
func hasSingleBit(n uint64) bool {
	return n&(n-1) == 0
}

// This project bit ceil, TODO: explain this func
func ThisBitCeil(val uint64) uint64 {
	if hasSingleBit(val) {
		return val
	}

	return 1 << (64 - bits.LeadingZeros64(val))
}

// interface: Uint
type Uint interface {
	uint8 | uint16 | uint32 | uint64
}

// This project Bit
func ThisBit[T Uint](val T, pos int) T {
	return (val >> pos) & 1
}

// This project Bits, returns a series of bit
func ThisBits[T Uint](val T, high T, low T) T {
	return (val >> low) & ((1 << (high - low + 1)) - 1)
}

// This project SignExtend
func ThisSignExtend(val uint64, size int) uint64 {
	return uint64(int64(val<<(63-size)) >> (63 - size))
}

// DONE
